﻿using Cinch;
using CodeSHARPer.Services;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Windows.Media;

namespace CodeSHARPer.Model
{
    public class FolderRoot : FolderItem
    {
        public override ObservableCollection<FolderItem> Children { get; set; }

        public static FolderRoot GetComputer(bool showFiles)
        {
            FolderRoot root = new FolderRoot();
            root.Name = "Computer";
            root.Image = FileInfoService.GetComputerImage(false);
            root.Children = new ObservableCollection<FolderItem>();

            DriveInfo[] drives = DriveInfo.GetDrives();
            StringBuilder sb = new StringBuilder();
            foreach (var drive in drives)
            {
                if (drive.IsReady)
                {
                    sb.Clear();
                    sb.AppendFormat("{0} ({1}) {2}",
                        drive.DriveType,
                        drive.Name,
                        drive.VolumeLabel);

                    FolderItem item = new FolderItem(drive.Name, showFiles);
                    item.Name = sb.ToString();
                    item.Image = FileInfoService.GetSmallImage(item.FullPath, false);
                    item.IsExpanded = true;

                    root.Children.Add(item);
                }
            }

            return root;
        }

        public static FolderRoot GetWorkingFolder(string path, bool showFiles)
        {
            FolderRoot root = new FolderRoot();

            if (Directory.Exists(path))
            {
                root.Name = Path.GetDirectoryName(path);
                root.Image = FileInfoService.GetFolderImage(false);
                root.Children = new ObservableCollection<FolderItem>();
                root.IsExpanded = true;

                DirectoryInfo di = new DirectoryInfo(path);
                foreach (var entries in di.GetFileSystemInfos())
                {
                    //fill the folderItem
                    FolderItem item = new FolderItem(entries.FullName, showFiles);
                    item.Name = entries.Name;
                    item.Image = entries.Attributes.HasFlag(FileAttributes.Directory) ? FileInfoService.GetFolderImage(false) : FileInfoService.GetSmallImage(entries.FullName, false);

                    root.Children.Add(item);
                }
            }

            return root;
        }
    }

    public class FolderItem : ViewModelBase
    {
        #region Fields

        ObservableCollection<FolderItem> children;
        bool isSelected;
        bool isExpanded;
        bool showFiles;
        ICollectionView view;

        #endregion

        #region Constructors

        public FolderItem() { }

        /// <summary>
        /// Represent a folder in the system directory
        /// </summary>
        /// <param name="path">The path of the folder displayed</param>
        /// <param name="showFiles">True to add files entries to the <seealso cref="Children"/>.</param>
        public FolderItem(string path, bool showFiles)
        {
            FullPath = path;
            this.showFiles = showFiles;

            //commands
            this.OpenContainerFolderCommand = new SimpleCommand<object, object>(DoOpenContainerFolder);
        }

        #endregion

        public string Name { get; set; }

        public string FullPath { get; set; }

        public ImageSource Image { get; set; }

        public bool IsSelected
        {
            get
            {
                return isSelected;
            }
            set
            {
                isSelected = value;
                this.NotifyPropertyChanged(ObservableHelper.CreateArgs<FolderItem>(c => c.IsSelected));
            }
        }
        public bool IsExpanded
        {
            get
            {
                return isExpanded;
            }
            set
            {
                isExpanded = value;
                this.NotifyPropertyChanged(ObservableHelper.CreateArgs<FolderItem>(c => c.IsExpanded));
            }
        }

        public bool HasChildren
        {
            get { return this.children != null && this.children.Count > 0; }
        }

        public virtual ObservableCollection<FolderItem> Children
        {
            get
            {
                if (children == null)
                {
                    children = new ObservableCollection<FolderItem>();
                    LoadChildren();
                    GetView();
                }

                return children;
            }
            set { children = value; }
        }

        private void GetView()
        {
            if (children != null && children.Count > 0)
            {
                view = CollectionViewSource.GetDefaultView(Children);
                view.Filter = FilterFiles;
            }
        }

        private void LoadChildren()
        {
            try
            {
                DirectoryInfo di = new DirectoryInfo(this.FullPath);

                if (Directory.Exists(this.FullPath))
                {
                    foreach (FileSystemInfo info in di.GetFileSystemInfos())
                    {
                        try
                        {
                            //do not show hiden entries
                            if (info.Attributes.HasFlag(FileAttributes.Hidden))
                                continue;

                            //do not show files if we don't want to
                            if (info.Attributes.HasFlag(FileAttributes.Directory) == false && showFiles == false)
                                continue;

                            //fill the folderItem
                            FolderItem item = new FolderItem(info.FullName, this.showFiles);
                            item.Name = info.Name;
                            item.Image = info.Attributes.HasFlag(FileAttributes.Directory) ? FileInfoService.GetFolderImage(false) : FileInfoService.GetSmallImage(info.FullName, false);
                            //item.IsSelected = info.FullName.Equals(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), StringComparison.OrdinalIgnoreCase);

                            //add to the children
                            children.Add(item);
                        }
                        catch (PathTooLongException) { }
                    }
                }
            }
            catch (UnauthorizedAccessException) { }
            catch (ArgumentException) { }
            catch (DirectoryNotFoundException) { }
        }

        string filterString;
        public string FilterString
        {
            get { return filterString; }
            set
            {
                filterString = value;
                if (view == null)
                    GetView();
                if (view != null)
                    view.Refresh();
            }
        }

        private bool FilterFiles(object filter)
        {
            FolderItem folder = filter as FolderItem;
            if (folder != null)
            {
                if (folder.HasChildren && folder.Children != null)
                {
                    ICollectionView viewChildren = CollectionViewSource.GetDefaultView(folder.Children);
                    if (string.IsNullOrEmpty(FilterString) == false)
                        viewChildren.Filter = (c =>
                            (c as FolderItem).FullPath.IndexOf(FilterString, 0, StringComparison.OrdinalIgnoreCase) >= 0);
                    else
                        viewChildren.Filter = (c => true);

                    foreach (var item in folder.Children.Where(c => c.HasChildren))
                    {
                        FilterFiles(item);
                    }
                }
            }

            return true;
        }

        #region commands
        public Cinch.SimpleCommand<object, object> OpenContainerFolderCommand { get; set; }
        internal void DoOpenContainerFolder(object args)
        {
            try
            {
                if (File.Exists(this.FullPath) || Directory.Exists(this.FullPath))
                {
                    string argument = @"/select, " + this.FullPath;

                    System.Diagnostics.Process.Start("explorer.exe", argument);
                }
            }
            catch (System.Exception ex)
            {
                ServiceResolver.Instance.MessageBoxService.ShowError(string.Format(CultureInfo.CurrentCulture, "{0}.", ex.Message), "Error open folder:");
            }
        }
        #endregion commands
    }
}
